using System;
using System.Collections.Generic;
using System.Linq;
using Drill.Configuration;
using Drill.Core;
using Drill.Core.MasterFactories;
using Drill.Extensibility;
using Drill.DrillBits.StructureMap.TestHelpers;
using Drill.TestSupport.TestDomains.AutomobileDomain;
using Drill.TestSupport.TestDomains.SimpleDomain;
using NUnit.Framework;
using Rhino.Mocks;
using StructureMap;

namespace Drill.DrillBits.StructureMap
{
	[TestFixture]
	public class StructureMapDrillBitTests
	{
		[Category("UnitTest")]
		[Test]
		public void CanConstructUsingConstructorWithInitializationContextParameter()
		{
			// Arrange
			IDrillBitInitContext initializationContext =
				StructureMapTestConfigs.CreateStructureMapProviderInitializationContext();

			// Act
			StructureMapDrillBit structureMapDrillBit = new StructureMapDrillBit(initializationContext);

			// Assert
			Assert.That(structureMapDrillBit, Is.Not.Null);
		}

		[Category("UnitTest")]
		[Test]
		public void CanConstructUsingParameterizedConstructor()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();

			// Act
			StructureMapDrillBit structureMapDrillBit = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			
			// Assert
			Assert.That(structureMapDrillBit, Is.Not.Null);
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "container", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotConstructUsingParameterizedConstructorWithNullStructureMapContainerParameterValue()
		{
			// Arrange
			// Nothing to arrange

			// Act
			// ReSharper disable ObjectCreationAsStatement
			new StructureMapDrillBitTestClass(null);
			// ReSharper restore ObjectCreationAsStatement

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Category("UnitTest")]
		[Test]
		public void CanCastAsIDrillBit()
		{
			// Arrange
			StructureMapDrillBit structureMapDrillBit = 
				new StructureMapDrillBit(new DrillBitInitContext());
			
			// Act
			// ReSharper disable RedundantCast
			IDrillBit containerProvider = structureMapDrillBit as IDrillBit;
			// ReSharper restore RedundantCast
			
			// Assert
			Assert.That(containerProvider, Is.Not.Null);
		}

		[Category("UnitTest")]
		[Test]
		public void CanCastAsIDependencyResolver()
		{
			// Arrange
			StructureMapDrillBit structureMapDrillBit = 
				new StructureMapDrillBit(new DrillBitInitContext());
			
			// Act
			// ReSharper disable RedundantCast
			IDependencyResolver containerProvider = structureMapDrillBit as IDependencyResolver;
			// ReSharper restore RedundantCast
			
			// Assert
			Assert.That(containerProvider, Is.Not.Null);
		}

		[Category("UnitTest")]
		[Test]
		public void CanGetInnerStructureMapContainerPropertyUsingDerivedClass()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();

			// Act
			StructureMapDrillBitTestClass drillBit = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			
			// Assert
			Assert.That(drillBit.InnerStructureMapContainer, Is.Not.Null);
		}

		[Category("UnitTest")]
		[Test]
		public void CanDispose()
		{
			// Arrange
			StructureMapDrillBit structureMapDrillBit = 
				new StructureMapDrillBit(new DrillBitInitContext());

			// Verify Arrangement
			Assert.That(structureMapDrillBit.IsDisposed, Is.False);
			
			// Act
			structureMapDrillBit.Dispose();

			// Assert
			Assert.That(structureMapDrillBit.IsDisposed, Is.True);
		}

		[Category("UnitTest")]
		[Test]
		public void CanDisposeWhenConstructedWithInitializationContext()
		{
			// Arrange
			StructureMapDrillBit structureMapDrillBit = 
				new StructureMapDrillBit(new DrillBitInitContext());

			// Verify Arrangement
			Assert.That(structureMapDrillBit.IsDisposed, Is.False);

			// Act
			structureMapDrillBit.Dispose();

			// Assert
			Assert.That(structureMapDrillBit.IsDisposed, Is.True);
		}

		[Category("IntegrationTest")]
		[Test]
		public void CanDisposeWithIntegrationTestConfig()
		{
			// Arrange
			IDependencyResolverConfiguration config =
				StructureMapTestConfigs.CreateStructureMapDependencyResolverConfigurationWithDefaultConfigurationSource();
			
			IDrillChuckDependencyResolver dependencyResolver = 
				(IDrillChuckDependencyResolver)DependencyResolverManager.Create(config);
			
			StructureMapDrillBit structureMapDrillBit = 
				dependencyResolver.DrillBits[0] as StructureMapDrillBit;

			// Verify Arrangement
			// ReSharper disable PossibleNullReferenceException
			Assert.That(structureMapDrillBit, Is.Not.Null);
			Assert.That(structureMapDrillBit.IsDisposed, Is.False);
			// ReSharper restore PossibleNullReferenceException

			// Act
			structureMapDrillBit.Dispose();

			// Assert
			Assert.That(structureMapDrillBit.IsDisposed, Is.True);
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ObjectDisposedException), ExpectedMessage = "has been disposed", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotInvokeInnerContainerAfterDispose()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			StructureMapDrillBitTestClass drillBit = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			Assert.That(drillBit.IsDisposed, Is.False);
			drillBit.Dispose();

			// Act
			// ReSharper disable UnusedVariable
			var container = drillBit.InnerStructureMapContainer;
			// ReSharper restore UnusedVariable

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ObjectDisposedException), ExpectedMessage = "has been disposed", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotInvokeGetAllInstancesAfterDispose()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			StructureMapDrillBit structureMapDrillBit = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			Assert.That(structureMapDrillBit.IsDisposed, Is.False);
			structureMapDrillBit.Dispose();
			IDependencyResolver dependencyResolver = structureMapDrillBit;

			// Act
			dependencyResolver.GetAllInstances(typeof(IVehicle));

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ObjectDisposedException), ExpectedMessage = "has been disposed", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotInvokeGetAllInstancesGenericAfterDispose()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			StructureMapDrillBit structureMapDrillBit = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			Assert.That(structureMapDrillBit.IsDisposed, Is.False);
			structureMapDrillBit.Dispose();
			IDependencyResolver dependencyResolver = structureMapDrillBit;

			// Act
			dependencyResolver.GetAllInstances<IVehicle>();

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ObjectDisposedException), ExpectedMessage = "has been disposed", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotInvokeGetInstanceAfterDispose()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			StructureMapDrillBit structureMapDrillBit = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			Assert.That(structureMapDrillBit.IsDisposed, Is.False);
			structureMapDrillBit.Dispose();
			IDependencyResolver dependencyResolver = structureMapDrillBit;

			// Act
			dependencyResolver.GetInstance(typeof (IVehicle));

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ObjectDisposedException), ExpectedMessage = "has been disposed", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotInvokeGetInstanceGenericAfterDispose()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			StructureMapDrillBit structureMapDrillBit = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			Assert.That(structureMapDrillBit.IsDisposed, Is.False);
			structureMapDrillBit.Dispose();
			IDependencyResolver dependencyResolver = structureMapDrillBit;

			// Act
			dependencyResolver.GetInstance<IVehicle>();

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ObjectDisposedException), ExpectedMessage = "has been disposed", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotInvokeGetInstanceByTypeAndNameAfterDispose()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			StructureMapDrillBit structureMapDrillBit = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			Assert.That(structureMapDrillBit.IsDisposed, Is.False);
			structureMapDrillBit.Dispose();
			IDependencyResolver dependencyResolver = structureMapDrillBit;

			// Act
			dependencyResolver.GetInstance(typeof(IVehicle), "car");

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ObjectDisposedException), ExpectedMessage = "has been disposed", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotInvokeGetInstanceByTypeAndNameGenericAfterDispose()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			StructureMapDrillBit structureMapDrillBit = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			Assert.That(structureMapDrillBit.IsDisposed, Is.False);
			structureMapDrillBit.Dispose();
			IDependencyResolver dependencyResolver = structureMapDrillBit;

			// Act
			dependencyResolver.GetInstance<IVehicle>("car");

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ObjectDisposedException), ExpectedMessage = "has been disposed", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotInvokeResolverObjectAfterDispose()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			StructureMapDrillBitTestClass drillBit = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			Assert.That(drillBit.IsDisposed, Is.False);
			drillBit.Dispose();

			// Act
			// ReSharper disable UnusedVariable
			var container = drillBit.ResolverObject;
			// ReSharper restore UnusedVariable

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Category("UnitTest")]
		[Test]
		public void CanGetInstanceByType()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();

			mockStructureMapContainer.Stub(c => c.GetInstance(typeof(IVehicle)))
							  .Return(new Car())
							  .Repeat.Any();

			IDependencyResolver dependencyResolver = new StructureMapDrillBitTestClass(mockStructureMapContainer);

			// Act
			IVehicle vehicle = dependencyResolver.GetInstance(typeof(IVehicle)) as IVehicle;

			// Assert
			Assert.That(vehicle, Is.Not.Null);
			Assert.That(vehicle, Is.TypeOf(typeof(Car)));
		}

		[Category("UnitTest")]
		[Test]
		public void CanGetInstanceByTypeGeneric()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();

			mockStructureMapContainer.Stub(c => c.GetInstance<IVehicle>())
							  .Return(new Car())
							  .Repeat.Any();

			IDependencyResolver dependencyResolver = new StructureMapDrillBitTestClass(mockStructureMapContainer);

			// Act
			IVehicle vehicle = dependencyResolver.GetInstance<IVehicle>();

			// Assert
			Assert.That(vehicle, Is.Not.Null);
			Assert.That(vehicle, Is.TypeOf(typeof(Car)));
		}

		[Category("IntegrationTest")]
		[Test]
		public void CanGetInstanceByTypeGenericWhenConfiguredWithSetupProvider()
		{
			// Arrange
			IDependencyResolverConfiguration config =
				StructureMapTestConfigs.CreateStructureMapDependencyResolverConfigurationWithSetupProvider();

			IDrillChuckDependencyResolver drillChuckDependencyResolver =
				(IDrillChuckDependencyResolver)new ConfigBasedMasterDependencyResolverFactory(config).CreateDependencyResolver();

			IDependencyResolver dependencyResolver = drillChuckDependencyResolver.DrillBits[0];

			// Act
			IVehicle vehicle = dependencyResolver.GetInstance<IVehicle>();

			// Assert
			Assert.That(vehicle, Is.Not.Null);
			Assert.That(vehicle, Is.TypeOf(typeof(Car)));
		}

		[Category("IntegrationTest")]
		[Test]
		public void CanGetInstanceByTypeGenericWhenConfiguredWithDefaultConfigurationSource()
		{
			// Arrange
			IDependencyResolverConfiguration config =
				StructureMapTestConfigs.CreateStructureMapDependencyResolverConfigurationWithDefaultConfigurationSource();

			IDrillChuckDependencyResolver drillChuckDependencyResolver =
				(IDrillChuckDependencyResolver)new ConfigBasedMasterDependencyResolverFactory(config).CreateDependencyResolver();

			IDependencyResolver dependencyResolver = drillChuckDependencyResolver.DrillBits[0];

			// Act
			ITestObjA testObjA = dependencyResolver.GetInstance<ITestObjA>();

			// Assert
			Assert.That(testObjA, Is.Not.Null);
			Assert.That(testObjA, Is.TypeOf(typeof(TestObjA)));
		}

		[Category("UnitTest")]
		[Test]
		public void CanGetInstanceByTypeAndName()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			
			mockStructureMapContainer.Stub(c => c.GetInstance(typeof(IVehicle), "car"))
							  .Return(new Car())
							  .Repeat.Any();

			mockStructureMapContainer.Stub(c => c.GetInstance(typeof(IVehicle), "truck"))
							  .Return(new Truck())
							  .Repeat.Any();

			IDependencyResolver dependencyResolver = new StructureMapDrillBitTestClass(mockStructureMapContainer);

			// Act
			IVehicle vehicle = dependencyResolver.GetInstance(typeof(IVehicle), "car") as IVehicle;

			// Assert
			Assert.That(vehicle, Is.Not.Null);
			Assert.That(vehicle, Is.TypeOf(typeof(Car)));

			// Act
			vehicle = dependencyResolver.GetInstance(typeof(IVehicle), "truck") as IVehicle;

			// Assert
			Assert.That(vehicle, Is.Not.Null);
			Assert.That(vehicle, Is.TypeOf(typeof(Truck)));
		}

		[Category("UnitTest")]
		[Test]
		public void CanGetInstanceByTypeAndNameGeneric()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();

			mockStructureMapContainer.Stub(c => c.GetInstance<IVehicle>("car"))
							  .Return(new Car())
							  .Repeat.Any();

			mockStructureMapContainer.Stub(c => c.GetInstance<IVehicle>("truck"))
							  .Return(new Truck())
							  .Repeat.Any();

			IDependencyResolver dependencyResolver = new StructureMapDrillBitTestClass(mockStructureMapContainer);

			// Act
			IVehicle vehicle = dependencyResolver.GetInstance<IVehicle>("car");

			// Assert
			Assert.That(vehicle, Is.Not.Null);
			Assert.That(vehicle, Is.TypeOf(typeof(Car)));

			// Act
			vehicle = dependencyResolver.GetInstance<IVehicle>("truck");

			// Assert
			Assert.That(vehicle, Is.Not.Null);
			Assert.That(vehicle, Is.TypeOf(typeof(Truck)));
		}

		[Category("UnitTest")]
		[Test]
		public void CanGetAllInstances()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			mockStructureMapContainer.Stub(c => c.GetAllInstances(typeof(IVehicle)))
							  .Return(new IVehicle[] { new Car(), new Truck() })
							  .Repeat.Any();

			IDependencyResolver dependencyResolver = new StructureMapDrillBitTestClass(mockStructureMapContainer);

			// Act
			IEnumerable<IVehicle> vehicles = dependencyResolver.GetAllInstances(typeof(IVehicle)).Cast<IVehicle>();

			// Assert
			// ReSharper disable PossibleMultipleEnumeration
			Assert.That(vehicles.Count(), Is.EqualTo(2));
			Assert.That(vehicles.Any(o => o is Car), Is.True);
			Assert.That(vehicles.Any(o => o is Truck), Is.True);
			// ReSharper restore PossibleMultipleEnumeration
		}

		[Category("UnitTest")]
		[Test]
		public void CanGetAllInstancesGeneric()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			mockStructureMapContainer.Stub(c => c.GetAllInstances<IVehicle>())
							  .Return(new IVehicle[] { new Car(), new Truck() })
							  .Repeat.Any();

			IDependencyResolver dependencyResolver = new StructureMapDrillBitTestClass(mockStructureMapContainer);

			// Act
			IEnumerable<IVehicle> vehicles = from v in dependencyResolver.GetAllInstances<IVehicle>() select v;

			// Assert
			// ReSharper disable PossibleMultipleEnumeration
			Assert.That(vehicles.Count(), Is.EqualTo(2));
			Assert.That(vehicles.Any(o => o is Car), Is.True);
			Assert.That(vehicles.Any(o => o is Truck), Is.True);
			// ReSharper restore PossibleMultipleEnumeration
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "targetType", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotGetAllInstancesWithNullTypeArgument()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			IDependencyResolver dependencyResolver = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			Type targetType = null;

			// Act
			// ReSharper disable ExpressionIsAlwaysNull
			dependencyResolver.GetAllInstances(targetType);
			// ReSharper restore ExpressionIsAlwaysNull

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "targetType", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotGetInstanceByTypeWithNullTypeArgument()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			IDependencyResolver dependencyResolver = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			Type targetType = null;

			// Act
			// ReSharper disable ExpressionIsAlwaysNull
			dependencyResolver.GetInstance(targetType);
			// ReSharper restore ExpressionIsAlwaysNull

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Category("UnitTest")]
		[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "targetType", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotGetInstanceByTypeAndNameWithNullTypeArgument()
		{
			// Arrange
			IContainer mockStructureMapContainer = MockRepository.GenerateStub<IContainer>();
			IDependencyResolver dependencyResolver = new StructureMapDrillBitTestClass(mockStructureMapContainer);
			Type targetType = null;

			// Act
			// ReSharper disable ExpressionIsAlwaysNull
			dependencyResolver.GetInstance(targetType, "car");
			// ReSharper restore ExpressionIsAlwaysNull

			// Assert
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}
	}
}
